using System;
using System.Collections.Generic;
using System.Text;
using System.Globalization;

namespace Toenda.Foundation.Globalization.Holiday.HolidayCalenderFactories {
	/// <summary>
	/// Implements the American HolidayCalendar
	/// </summary>
	public class AmericanHolidayCalendarFactory : HolidayCalendarFactory {
		private static AmericanHolidayCalendarFactory _instance;

		/// <summary>
		/// Gets the instance fo the specific holiday calendar
		/// </summary>
		/// <param name="year"></param>
		/// <param name="eventType"></param>
		/// <returns></returns>
		public static AmericanHolidayCalendarFactory GetInstance(int year, HolidayEventType eventType) {
			if(_instance == null || year != _instance.CurrentYear) {
				_instance = new AmericanHolidayCalendarFactory(year, eventType);
				return _instance;
			}

			return _instance;
		}

		/// <summary>
		/// Creates a new instance of <see cref="AmericanHolidayCalendarFactory"/> and adds the specific holidays
		/// </summary>
		/// <param name="year"></param>
		/// <param name="eventType"></param>
		private AmericanHolidayCalendarFactory(int year, HolidayEventType eventType) {
			this.CurrentYear = year;

			this.Initialize(eventType);
		}

		/// <summary>
		/// Initialize the calendar
		/// </summary>
		/// <param name="eventType"></param>
		public override void Initialize(HolidayEventType eventType) {
			base.Holidays = new List<Holiday>();
			base.Holidays.Add(new Holiday(true, new DateTime(this.CurrentYear, 1, 1), "New Year"));
			base.Holidays.Add(new Holiday(true, new DateTime(this.CurrentYear, 7, 4), "Independence Day"));
			base.Holidays.Add(new Holiday(true, new DateTime(this.CurrentYear, 11, 11), "Veterans Day"));
			base.Holidays.Add(new Holiday(true, new DateTime(this.CurrentYear, 12, 25), "Christmas Day"));

			base.Holidays.Add(new Holiday(false, GetMartinLutherKingDay(), "Martin Luther King Day"));
			base.Holidays.Add(new Holiday(false, GetWashingtonsBirthday(), "Washington's Birthday"));
			base.Holidays.Add(new Holiday(false, GetMemorialDay(), "Memorial Day"));
			base.Holidays.Add(new Holiday(false, GetLaborDay(), "Labor Day"));
			base.Holidays.Add(new Holiday(false, GetColumbusDay(), "Columbus Day"));
			base.Holidays.Add(new Holiday(false, GetThanksgivingDay(), "Thanksgiving Day"));

			// event dates
			switch(eventType) {
				case HolidayEventType.Banking:
					break;

				case HolidayEventType.Currency:
					break;

				case HolidayEventType.Settlement:
					break;

				case HolidayEventType.Trading:
					break;
			}
		}

		/// <summary>
		/// Get the Martin-Luther-King-Day
		/// </summary>
		/// <returns></returns>
		private DateTime GetMartinLutherKingDay() {
			int count = 0;
			DateTime tmp = new DateTime(base.CurrentYear, 1, 1);
			GregorianCalendar gc = new GregorianCalendar();

			for(int i = 1; i <= gc.GetDaysInMonth(base.CurrentYear, 1); i++) {
				if(tmp.DayOfWeek == DayOfWeek.Monday) {
					count++;
				}

				if(count == 3) {
					break;
				}

				tmp = tmp.AddDays(1);
			}

			return tmp;
		}

		/// <summary>
		/// Get the Washingtons-Birthday
		/// </summary>
		/// <returns></returns>
		private DateTime GetWashingtonsBirthday() {
			int count = 0;
			DateTime tmp = new DateTime(base.CurrentYear, 2, 1);
			GregorianCalendar gc = new GregorianCalendar();

			for(int i = 1; i <= gc.GetDaysInMonth(base.CurrentYear, 2); i++) {
				if(tmp.DayOfWeek == DayOfWeek.Monday) {
					count++;
				}

				if(count == 3) {
					break;
				}

				tmp = tmp.AddDays(1);
			}

			return tmp;
		}

		/// <summary>
		/// Get the Memorial-Day
		/// </summary>
		/// <returns></returns>
		private DateTime GetMemorialDay() {
			GregorianCalendar gc = new GregorianCalendar();
			int daysInMonth = gc.GetDaysInMonth(base.CurrentYear, 5);
			DateTime tmp = new DateTime(base.CurrentYear, 5, daysInMonth);

			for(int i = daysInMonth; i >= 1; i--) {
				if(tmp.DayOfWeek == DayOfWeek.Monday) {
					break;
				}

				tmp = tmp.AddDays(-1);
			}

			return tmp;
		}

		/// <summary>
		/// Get the Labor-Day
		/// </summary>
		/// <returns></returns>
		private DateTime GetLaborDay() {
			DateTime tmp = new DateTime(base.CurrentYear, 9, 1);
			GregorianCalendar gc = new GregorianCalendar();

			for(int i = 1; i <= gc.GetDaysInMonth(base.CurrentYear, 9); i++) {
				if(tmp.DayOfWeek == DayOfWeek.Monday) {
					break;
				}

				tmp = tmp.AddDays(1);
			}

			return tmp;
		}

		/// <summary>
		/// Get the Columbus-Day
		/// </summary>
		/// <returns></returns>
		private DateTime GetColumbusDay() {
			int count = 0;
			DateTime tmp = new DateTime(base.CurrentYear, 10, 1);
			GregorianCalendar gc = new GregorianCalendar();

			for(int i = 1; i <= gc.GetDaysInMonth(base.CurrentYear, 10); i++) {
				if(tmp.DayOfWeek == DayOfWeek.Monday) {
					count++;
				}

				if(count == 2) {
					break;
				}

				tmp = tmp.AddDays(1);
			}

			return tmp;
		}

		/// <summary>
		/// Get the Thanksgiving-Day
		/// </summary>
		/// <returns></returns>
		private DateTime GetThanksgivingDay() {
			int count = 0;
			DateTime tmp = new DateTime(base.CurrentYear, 11, 1);
			GregorianCalendar gc = new GregorianCalendar();

			for(int i = 1; i <= gc.GetDaysInMonth(base.CurrentYear, 11); i++) {
				if(tmp.DayOfWeek == DayOfWeek.Thursday) {
					count++;
				}

				if(count == 4) {
					break;
				}

				tmp = tmp.AddDays(1);
			}

			return tmp;
		}
	}
}
